package cn.com.zero.common.auth.service.impl;

import cn.com.zero.api.auth.util.UserCacheKeyUtil;
import cn.com.zero.api.auth.vo.LockedUserVO;
import cn.com.zero.base.util.RedisUtil;
import cn.com.zero.common.auth.service.ILockedUserService;
import cn.com.zero.db.vo.PageParamsVO;
import cn.com.zero.db.vo.PageVO;
import cn.hutool.core.util.StrUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;

/**
 * @author xiongxj
 * @version 1.0.0
 * @Description 锁定用户相关操作的service实现类
 * @createTime 2023/3/29 15:27
 */
public abstract class AbstractLockedUserService implements ILockedUserService {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());
    private RedisUtil<LockedUserVO> redisUtil;

    @Override
    public PageVO<LockedUserVO> getLockedUsers(String userBelongToSide, PageParamsVO pageParams,
                                               List<String> searchUserIds, String loginIp,
                                               Date lockedBeginTime, Date lockedEndTime) {
        PageVO<LockedUserVO> pageResult = new PageVO<>();
        List<LockedUserVO> lockedUserResult = new ArrayList<>();
        pageResult.setTotal(0);
        pageResult.setPages(0);
        pageResult.setList(lockedUserResult);

        // 获取分页参数
        Integer pageNum = pageParams.getPageNum();
        Integer pageSize = pageParams.getPageSize();
        if (pageSize == null || pageSize.compareTo(0) <= 0 || pageNum == null || pageNum.compareTo(0) <= 0) {
            // 分页参数不合法，直接返回空
            logger.debug("获取[{}]端锁定用户数据时，分页参数不合法，返回空集", userBelongToSide);
            return pageResult;
        }

        // 从redis获取数据
        Set<String> lockedUserIdSet = new HashSet<>();
        List<LockedUserVO> allLockedUsers = getAllLockedUsers(userBelongToSide, lockedUserIdSet);
        if (CollectionUtils.isEmpty(allLockedUsers)) {
            return pageResult;
        }

        // 从数据库中获取详细信息，组装redis中的在线信息
        Map<String, LockedUserVO> userDetailMap = getUsers(new ArrayList<>(lockedUserIdSet));
        // 对在线用户信息按照过滤条件进行过滤
        lockedUserResult = filterLockedUserBySearch(allLockedUsers, userDetailMap,
                searchUserIds, loginIp, lockedBeginTime, lockedEndTime);

        if (CollectionUtils.isEmpty(lockedUserResult)) {
            return pageResult;
        }

        // 对符合条件的数据进行排序
        String orderBy = pageParams.getOrderBy();
        sort(lockedUserResult, orderBy);

        // 符合条件的总数
        int totalSize = lockedUserResult.size();
        pageResult.setTotal(totalSize);
        // 分页对应的数据索引下标
        int beginIndex = (pageNum - 1) * pageSize;
        int endIndex = pageNum * pageSize - 1;
        if (endIndex > totalSize - 1) {
            endIndex = totalSize - 1;
        }

        // 按照分页下标索引获取数据
        List<LockedUserVO> pageListResult = new ArrayList<>();
        for (int index = beginIndex; index <= endIndex; index++) {
            pageListResult.add(lockedUserResult.get(index));
        }
        pageResult.setPages(totalSize % pageSize == 0 ? totalSize / pageSize : totalSize / pageSize + 1);
        pageResult.setList(pageListResult);
        return pageResult;
    }

    @Override
    public boolean unlockUser(String userBelongToSide, String userId) {
        Assert.notNull(userBelongToSide, "用户所属端不允许为空");
        Assert.notNull(userId, "用户ID不允许为空");
        // 清除锁定用户信息
        String lockedUserKey = UserCacheKeyUtil.getLockedUserCacheKey(userBelongToSide, userId);
        redisUtil.delete(lockedUserKey);

        // 清除用户的登录错误日志
        Integer deleteCount = deleteErrorLoginLog(userId);
        if (deleteCount == 0) {
            return false;
        }
        return true;
    }

    /**
     * 获取指定的用户信息
     *
     * @param userIds 用户ID集合
     * @return 用户详细信息Map集合，key为用户id，value为用户详细信息
     */
    protected abstract Map<String, LockedUserVO> getUsers(List<String> userIds);

    /**
     * 清除用户的错误登录日志信息
     *
     * @param userId 用户ID
     * @return 成功删除的行数
     */
    protected abstract Integer deleteErrorLoginLog(String userId);

    /**
     * 获取所有的锁定用户信息
     *
     * @param userBelongToSide 用户所属端
     * @param allUserIds       锁定用户的用户ID集合
     * @return 所有的锁定用户信息数据
     */
    private List<LockedUserVO> getAllLockedUsers(String userBelongToSide, Set<String> allUserIds) {
        List<LockedUserVO> lockedUserList = new ArrayList<>();

        // 从redis缓存中获取所有的锁定用户信息
        String lockedUserCacheKey = UserCacheKeyUtil.getLockedUserCacheKey(userBelongToSide, null);
        Set<String> lockedUserCacheKeys = redisUtil.keys(lockedUserCacheKey);
        if (CollectionUtils.isEmpty(lockedUserCacheKeys)) {
            return lockedUserList;
        }
        for (String userCacheKey : lockedUserCacheKeys) {
            LockedUserVO lockedUser = redisUtil.getObject(userCacheKey);
            if (null == lockedUser) {
                continue;
            }
            lockedUserList.add(lockedUser);
            allUserIds.add(lockedUser.getUserId());
        }
        return lockedUserList;
    }

    /**
     * 按照搜索条件对锁定用户进行过滤处理，同时拼装用户的详细信息
     *
     * @param lockedUserList  原始的锁定用户列表
     * @param userDetailMap   用户详细信息
     * @param searchUserIds   待过滤的用户ID列表
     * @param loginIp         待过滤的用户登录ip
     * @param lockedBeginTime 待过滤的用户锁定开始时间
     * @param lockedEndTime   待过滤的用户锁定结束时间
     * @return 已经过滤处理后的锁定用户信息
     */
    private List<LockedUserVO> filterLockedUserBySearch(List<LockedUserVO> lockedUserList, Map<String, LockedUserVO> userDetailMap,
                                                        List<String> searchUserIds, String loginIp,
                                                        Date lockedBeginTime, Date lockedEndTime) {
        List<LockedUserVO> afterFilteredList = new ArrayList<>();

        for (LockedUserVO lockedUserVO : lockedUserList) {
            LockedUserVO lockedUserDB = userDetailMap.get(lockedUserVO.getUserId());
            if (null == lockedUserDB) {
                continue;
            }
            String userId = lockedUserVO.getUserId();
            LockedUserVO lockedUser = lockedUserDB.clone(lockedUserVO);
            // 按照搜索条件 用户ID 进行过滤
            if (!CollectionUtils.isEmpty(searchUserIds)) {
                if (searchUserIds.contains(userId)) {
                    afterFilteredList.add(lockedUser);
                }
            } else {
                afterFilteredList.add(lockedUser);
            }
            // 按照搜索条件 登录IP 进行过滤
            if (!StrUtil.isEmpty(loginIp)) {
                if (!lockedUser.getLoginIp().contains(loginIp)) {
                    afterFilteredList.remove(lockedUser);
                }
            }
            // 按照搜索条件 锁定开始时间和锁定结束时间进行过滤
            if (lockedBeginTime != null && lockedEndTime != null) {
                if (lockedUser.getLockedTime().compareTo(lockedBeginTime) < 0
                        || lockedUser.getLockedTime().compareTo(lockedEndTime) > 0) {
                    afterFilteredList.remove(lockedUser);
                }
            }
        }
        return afterFilteredList;
    }

    /**
     * 对锁定用户进行排序
     * 目前只支持锁定时间倒序或者正序，若锁定时间相同，则按照用户升序
     *
     * @param sortList 待排序的锁定用户列表
     * @param orderBy  排序条件
     */
    private void sort(List<LockedUserVO> sortList, String orderBy) {
        if (orderBy != null && orderBy.trim().equalsIgnoreCase("loginTime desc")) {
            // 按照锁定时间倒序
            sortList.sort((onlineUser1, onlineUser2) -> {
                if (onlineUser1.getLockedTime().compareTo(onlineUser2.getLockedTime()) == 0) {
                    return onlineUser1.getUserId().compareTo(onlineUser2.getUserId());
                }
                return onlineUser1.getLockedTime().before(onlineUser2.getLockedTime()) ? 1 : -1;
            });

        } else {
            // 按照锁定时间正序
            sortList.sort((onlineUser1, onlineUser2) -> {
                if (onlineUser1.getLockedTime().compareTo(onlineUser2.getLockedTime()) == 0) {
                    return onlineUser1.getUserId().compareTo(onlineUser2.getUserId());
                }
                return onlineUser1.getLockedTime().before(onlineUser2.getLockedTime()) ? -1 : 1;
            });
        }
    }


    @Resource
    public void setRedisUtil(RedisUtil<LockedUserVO> redisUtil) {
        this.redisUtil = redisUtil;
    }
}
